<?php
/**
 * JWT相关
 */
declare(strict_types=1);

namespace App\Helper;

use App\Exception\BusinessException;
use Firebase\JWT\JWT;
use Hyperf\Utils\Context;
use Hyperf\Utils\Str;
use Psr\Http\Message\ServerRequestInterface;
use Throwable;

class JwtHelper
{
    const ADMIN_JWT_TOKEN = 'admin_jwt_token';
    const GUEST_JWT_TOKEN = 'guest_jwt_token';

    /**
     * 生成
     * @param mixed $info
     * @param string $type
     * @return string
     */
    public static function encode($info, string $type = 'HS256'): string
    {
        $data = [
            'data' => $info,
            'iat' => time(),
            'jti' => Str::random(8),
        ];
        return JWT::encode($data, self::getKey($type), $type);
    }

    /**
     * 获取密钥
     * @param string $type
     * @return string
     */
    private static function getKey(string $type = 'HS256'): string
    {
        $keyName = 'JWT_' . $type . '_KEY';
        $key = env($keyName);
        if (!$key) {
            throw new BusinessException(0, '请在 .env 配置 ' . $keyName . '=你的KEY ！');
        }
        return $key;
    }

    /**
     * 从请求中解密
     * @param string $key
     * @param ServerRequestInterface|null $request
     * @param string $type
     * @return object|null
     */
    public static function decodeWithRequest(string $key = self::ADMIN_JWT_TOKEN, ?ServerRequestInterface $request = null, string $type = 'HS256'): ?object
    {
        if (empty($key)) {
            return null;
        }
        $request || $request = Context::get(ServerRequestInterface::class);
        if (!$request) {
            return null;
        }
        foreach (['getQueryParams', 'getCookieParams', 'getHeaders', 'getParsedBody'] as $fun) {
            $array = $request->{$fun}();
            $jwt = (is_array($array) ? ($array[$key] ?? null) : null);
            if ($jwt) {
                return self::decode($jwt, $type);
            }
        }
        return null;
    }

    /**
     * 解密
     * @param string $token
     * @param string $type
     * @return object|null
     */
    public static function decode(string $token, string $type = 'HS256'): ?object
    {
        if (empty($token)) {
            return null;
        }
        try {
            return JWT::decode($token, self::getKey($type), [$type]);
        } catch (Throwable $throwable) {
            return null;
        }
    }

    public static function dataToHash(?object $jwtData): ?string
    {
        if (is_null($jwtData)) {
            return null;
        }
        $jti = $jwtData->jti ?? null;
        if (is_null($jti)) {
            return null;
        }
        $jwtData = json_encode($jwtData);
        return $jti . md5($jwtData);
    }
}
